package com.socio.utils;

import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

import com.socio.model.Day;
import com.socio.model.Event;
import com.socio.model.User;

/**
 * Class is responsible for updating fields for the object gotten from db
 * @author potlow
 *
 */
public class FieldAccessor {

	public static enum FieldAccessorField {
		DayUser {
			public void accessField(Object objectWithFieldToAccess) {
				
				verifyObjectIsValid(objectWithFieldToAccess, Day.class, "Access Object");
				Day day = (Day)objectWithFieldToAccess;
				
				day.getUser().getKey();
			}
			
			public void updateField(Object objectToUpdate, Object updatedValue) {}
		},
		UserDays {
			public void accessField(Object objectWithFieldToAccess) {
				
				verifyObjectIsValid(objectWithFieldToAccess, User.class, "Access Object");
				User user = (User)objectWithFieldToAccess;
				
				Set<Day> days = user.getDays();
				if(days != null) {
			        for(Day day : days)
			        	day.getKey();
		        }
			}
			
			public void updateField(Object objectToUpdate, Object updatedValue) {
				
				verifyObjectIsValid(updatedValue, Day.class, "Update Value");
				Day day = (Day) updatedValue;
		        accessField(objectToUpdate);
		        User userToUpdate = (User)objectToUpdate;
		        
		        // update days
		        Set<Day> days = userToUpdate.getDays();
		        if(days == null) {
		        	Set<Day> newDaySet = new LinkedHashSet<Day>();
		        	newDaySet.add(day);
		        	userToUpdate.setDays(newDaySet);
		        } else if(!days.contains(day)) {
		        	days.add(day);
		        } else {
		        	// update most recent day
		        	Day dayToUpdate = days.iterator().next();
		        	dayToUpdate.getEvents().addAll(day.getEvents());
		        	dayToUpdate.setQuickScore(dayToUpdate.getQuickScore() + day.getQuickScore());
		        }
		        
		        // update user quick score
		        for(Event event: day.getEvents())
		        	event.getType().incrementScores(userToUpdate);
			}
		},
		UserFacebookID {
			public void accessField(Object objectWithFieldToAccess) {}
			
			public void updateField(Object objectToUpdate, Object updatedValue) {
				
				verifyObjectIsValid(updatedValue, String.class, "Update Value");
				String facebookID = (String) updatedValue;
				verifyObjectIsValid(objectToUpdate, User.class, "Access Object");
		        User userToUpdate = (User)objectToUpdate;
		        
		        userToUpdate.setFacebookID(facebookID);
				
			}
		},
		UserNickname {
			public void accessField(Object objectWithFieldToAccess) {}
			
			public void updateField(Object objectToUpdate, Object updatedValue) {
				
				verifyObjectIsValid(updatedValue, String.class, "Update Value");
				String nickname = (String) updatedValue;
				verifyObjectIsValid(objectToUpdate, User.class, "Access Object");
		        User userToUpdate = (User)objectToUpdate;
		        
		        userToUpdate.setNickname(nickname);
				
			}
		};
		
		/**
		 * Touches a field of an attached object
		 * 
		 * @param objectWithFieldToAccess object with field to touch
		 */
		public abstract void accessField(Object objectWithFieldToAccess);
		
		/**
		 * Updates a field of an attached object
		 * 
		 * @param objectToUpdate object to update
		 * @param updateValue update value
		 */
		public abstract void updateField(Object objectToUpdate, Object updateValue);
		
		/**
		 * Check to see if an object is of the right type and is not empty
		 * 
		 * @param object object to verify
		 * @param validObjectClass class object needs to be an instance of
		 * @param type Type of object being verified
		 */
		protected void verifyObjectIsValid(Object object, Class<? extends Object> validObjectClass,
				String type) {
			if (object.getClass() != validObjectClass)
	            throw new IllegalArgumentException(type +" must be a "+ validObjectClass.getCanonicalName());
		}
	};
	
	/**
	 * Access fields of specified object while still under the management of the persistence manager
	 * 
	 * @param object object with fields to update
	 * @param updateFields Fields of object to update
	 * @param updateValues Values to update object with
	 * @param touchFields Fields of object to be touched
	 */
	public static void accessFields(Object object, List<FieldAccessorField> updateFields, 
			List<Object> updateValues, List<FieldAccessorField> touchFields) {
		
		// update fields if there are any
		if(updateFields != null) {
			int updateFieldIndex = 0;
			for(FieldAccessorField updateField: updateFields) {
				updateField.updateField(object, updateValues.get(updateFieldIndex));
				updateFieldIndex++;
			}
		}
		
		// touch fields if there are any
		if(touchFields != null) {
			for(FieldAccessorField touchField : touchFields)
				touchField.accessField(object);
		}
	}
}
